#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

#include <boost/format.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/support_argument.hpp>

#include "p-machine.hh"

using namespace boost::spirit;
using namespace boost::spirit::qi;
using namespace boost::spirit::ascii;

using boost::phoenix::ref;
using boost::phoenix::push_back;
using boost::phoenix::val;
using boost::phoenix::construct;


template <typename Iterator>
struct calculator : grammar<Iterator, boost::spirit::ascii::space_type> {
  calculator(std::vector<int>& code) : calculator::base_type(expression), code(code) {
    expression =
      (lit("if") > bool_exprs > lit("then")) [push_back(ref(code), 0x300)]
      >> *(action)
      ;
    expression.name("expression");
    
    bool_exprs = bool_expr 
      >> *( lit("and") > bool_expr [push_back(ref(code), 0x2)])
      ;
    bool_exprs.name("bool expressions");
    
    bool_expr = cond_left > '=' > number [push_back(ref(code), 0x4)]
      ;
    bool_expr.name("boolean expression");
    
    cond_left =
      ( (lit("location") | lit("here")) [push_back(ref(code), pcode_load_here)] )
      | (lit("verb") [push_back(ref(code), pcode_load_verb)] )
      | (lit("obj1") [push_back(ref(code), pcode_load_obj1)] ) 
      | (lit("obj2") [push_back(ref(code), pcode_load_obj2)] ) 
      | (lit("adj1") [push_back(ref(code), pcode_load_adj1)] ) 
      | (lit("adj2") [push_back(ref(code), pcode_load_adj2)] ) 
      ;
    cond_left.name("left hand side of a condition");
    
    number = uint_ [
		    push_back(ref(code), pcode_load_immediate),
		    push_back(ref(code), _1)
		    ]
      ;
    number.name("number");

    action = 
      ( lit("long") [push_back(ref(code), 0x80)] )
      | ( lit("short") [push_back(ref(code), 0x81)] )
      | ( lit("write") > number ) [push_back(ref(code), 0x82)]
      ;
    action.name("action");
    
    // term =
    //     factor
    //     >> *(   ('*' > factor           [push_back(ref(code), op_mul)])
    //         |   ('/' > factor           [push_back(ref(code), op_div)])
    //         )
    //     ;
    
    // factor =
    //     uint_                           [
    //                                         push_back(ref(code), op_int),
    //                                         push_back(ref(code), _1)
    //                                     ]
    //     |   '(' > expression > ')'
    //     |   ('-' > factor               [push_back(ref(code), op_neg)])
    //     |   ('+' > factor)
    //     ;
    
    on_error<fail>
      (
       expression
       , std::cout
       << val("Error! Expecting ")
       << _4                               // what failed?
       << val(" here: \"")
       << construct<std::string>(_3, _2)   // iterators to error-pos, end
       << val("\"")
       << std::endl
       );
  }
  
  rule<Iterator, boost::spirit::ascii::space_type> expression, bool_exprs, bool_expr, number, cond_left, action;
  std::vector<int>& code;
};

template <typename Grammar>
bool compile(Grammar const& calc, std::string const& expr) {
  std::string::const_iterator iter = expr.begin();
  std::string::const_iterator end = expr.end();
  bool r = phrase_parse(iter, end, calc, boost::spirit::ascii::space);
  
  if (r && iter == end) {
    std::cout << "-------------------------\n";
    std::cout << "Parsing succeeded\n";
    std::cout << "-------------------------\n";
    return true;
  } else {
    std::cout << "-------------------------\n";
    std::cout << "Parsing failed\n";
    std::cout << "-------------------------\n";
    return false;
  }
}

int main() {
  std::cout << "Expression compiler...\n\n";
  std::cout << "Type an expression...or [q or Q] to quit\n\n";

  typedef std::string::const_iterator iterator_type;
  typedef calculator<iterator_type> calculator;

  std::vector<int> code;          //  Our VM code
  calculator calc(code);          //  Our grammar

  std::string str;
  while (std::getline(std::cin, str))
    {
      if (str.empty() || str[0] == 'q' || str[0] == 'Q')
	break;

      code.clear();
      if (::compile(calc, str)) {
	for(std::vector<int>::const_iterator it = code.begin(); it != code.end(); ++it) {
	  std::cout << boost::format(" %04X") % *it;
	}
	std::cout << std::endl;
      }
    }
  return 0;
}


